/*
 Copyright (c) 2013 MeherTJ G. All rights reserved.
 License: LGPL for personnal study or free software.
 */

#pragma once

#include <cstdint>
#include <algorithm>

#ifdef ZEN_COLOR_EX_VEC4
#include "zen_vector.h"
#endif

namespace Zen 
{
	enum class ePixel : int
	{
		None  = 0,
		Grey  = 1,	// grey 1 byte
		GA    = 2,	// grey+alpha 2 bytes
		RGB   = 3,	// 3 bytes
		RGBA  = 4,	// 4 bytes
	};
	
	class Color4b;
	class Color4f;
	class ColorRBG;
	
	/**
	 enum usual color's name in RGB format.
	 */
	enum class eColor : uint32_t
	{
		Black     = 0x000000,
		White     = 0xFFFFFF,
		Grey      = 0x808080,

		Red       = 0xFF0000,
		Orange    = 0xFF7F00,
		Yellow    = 0xFFFF00,
		Green     = 0x00FF00,
		Cyan      = 0x00FFFF,
		Blue      = 0x0000FF,
		Magenta   = 0xFF00FF,
	};

	class Color2b
	{
	public:
		uint8_t grey8 = 255, a8 = 255;

		Color2b() {}

		Color2b(uint8_t g, uint8_t a) {
			grey8 = g;
			a8 = a;
		}
	};

	class Color3b // ePixel::RGB
	{
	public:
		uint8_t r8 = 255, g8 = 255, b8 = 255;

	public:
		Color3b()
		{
		}
		Color3b(eColor e)
		{
			this->set((uint32_t)e);
		}
		Color3b(uint32_t rgb) // 0xrrggbb
		{
			this->set(rgb);
		}
		Color3b(uint8_t r, uint8_t g, uint8_t b)
		{
			this->set(r, g, b);
		}

		void set(uint32_t rgb)
		{
			b8 = rgb & 0xff;
			g8 = rgb >> 8 & 0xff;
			r8 = rgb >> 16 & 0xff;
		}
		void set(uint8_t r, uint8_t g, uint8_t b)
		{
			r8 = r;
			g8 = g;
			b8 = b;
		}
		
		bool operator == (Color3b const & o) const
		{
			return r8 == o.r8 && g8 == o.g8 && b8 == o.b8;
		}
		
		bool operator != (Color3b const & o) const
		{
			return !(*this == o);
		}
	};


	/* C: Color4b
	 - 32bits Color4b in RGBA format, 0xRRGGBBAA.
	 */

	class Color4b
	{
	public:
		uint8_t r8 = 255, g8 = 255, b8 = 255, a8 = 255;

	public:
		Color4b()
		{
		}

		Color4b(Color2b ga)
		{
			this->set(ga);
		}
		Color4b(eColor e)
		{
			this->set(Color3b(e));
		}
		/* 0xRRGGBBAA */
		Color4b(uint32_t rgba)
		{
			this->set(rgba);
		}
		
		Color4b(Color3b const & rgb)
		{
			this->set(rgb);
		}

		Color4b(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
		{
			this->set(r, g, b, a);
		}

		void set(Color3b const & rgb)
		{
			r8 = rgb.r8;
			g8 = rgb.g8;
			b8 = rgb.b8;
		}
		void set(Color2b const & ga)
		{
			r8 = g8 = b8 = ga.grey8;
			a8 = ga.a8;
		}

		void set(eColor color)
		{
			this->set(Color3b(color));
		}
		
		void set(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
		{
			r8 = r;
			g8 = g;
			b8 = b;
			a8 = a;
		}

		/* 0xRRGGBBAA */
		void set(uint32_t rgba)
		{
			r8 = rgba >> 24;
			g8 = rgba >> 16 & 0xff;
			b8 = rgba >> 8 & 0xff;
			a8 = rgba & 0xff;
		}
		
		/* 0xAARRGGBB */
		void setARGB(uint32_t argb)
		{
			a8 = argb >> 24;
			r8 = argb >> 16 & 0xff;
			g8 = argb >> 8 & 0xff;
			b8 = argb & 0xff;
		}
		
		void setAlpha(uint8_t a)
		{
			a8 = a;
		}

		void setGrey(uint8_t grey)
		{
			r8 = g8 = b8 = grey;
		}
		
		bool operator == (Color4b const & o)
		{
			return *(int32_t*)(void*)this == *(int32_t*)(void*)&o;
		}
		
		bool operator != (Color4b const & o)
		{
			return !(*this == o);
		}

		float af() const
		{
			return a8 / (float)0xff;
		}
		float rf() const
		{
			return r8 / (float)0xff;
		}
		float gf() const
		{
			return g8 / (float)0xff;
		}
		float bf() const
		{
			return b8 / (float)0xff;
		}

		operator Color3b ()
		{
			return Color3b(r8, g8, b8);
		}
	};
	

	 
	/** Color4f
	 -- Color4b by 4 float
	 */
	struct Color4f
	{
	public:
		float red = 1.f, green = 1.f, blue = 1.f, alpha = 1.f;
		
	public:
		Color4f()
		{
		}
		Color4f(eColor e)
		{
			this->set(e);
		}

		Color4f(uint32_t value)
		{
			this->set(Color4b(value));
		}
		Color4f(Color4b const & color)
		{
			this->set(color);
		}

		Color4f(float r, float g, float b, float a)
		{
			this->set(r, g, b, a);
		}

		void set(eColor e)
		{
			this->set(Color4b(e));
		}

		void set(float r, float g, float b, float a)
		{
			red = r;
			green = g;
			blue = b;
			alpha = a;
		}
		void set(Color4b const & color)
		{
			red = color.rf();
			green = color.gf();
			blue = color.bf();
			alpha = color.af();
		}
		void set(uint32_t rgba)
		{
			set(Color4b(rgba));
		}

		void operator *= (Color4f const & o)
		{
			red *= o.red;
			green *= o.green;
			blue *= o.blue;
			alpha *= o.alpha;
		}

		/// operator * + -
		Color4f operator * (Color4f const & o) const
		{
			return Color4f(red * o.red, green * o.green, blue * o.blue, alpha * o.alpha);
		}
		Color4f operator + (Color4f const & o) const
		{
			return Color4f(red + o.red, green + o.green, blue + o.blue, alpha + o.alpha);
		}

		Color4f operator - (Color4f const & o) const
		{
			return Color4f(red - o.red, green - o.green, blue - o.blue, alpha - o.alpha);
		}

		// operator == !=
		bool operator == (Color4f const & o) const {
			return red == o.red && green == o.green && blue == o.blue && alpha == o.alpha;
		}
		bool operator != (Color4f const & o) const {
			return !(*this == o);
		}

		// Color4b convert
		operator Color4b() const {
			return Color4b {
					(uint8_t)std::min(red * 255.f, 255.f),
					(uint8_t)std::min(green * 255.f, 255.f),
					(uint8_t)std::min(blue * 255.f, 255.f),
					(uint8_t)std::min(alpha * 255.f, 255.f),
			};
		}

#ifdef ZEN_COLOR_EX_VEC4
		Color4f(Vec4 const & v) {
			red = v.x;
			green = v.y;
			blue = v.z;
			alpha = v.w;
		}
		operator Vec4() const {
			return { red, green, blue, alpha };
		}
#endif
	};
	/// Lerp.
	inline float ColorLerp(float start, float end, float v)
	{
		return start + (end - start) * v;
	}
	inline Color4f Color4fLerp(Color4f const & c0, Color4f const & c1, float value)
	{
		return Color4f
		(
		 ColorLerp(c0.red, c1.red, value),
		 ColorLerp(c0.green, c1.green, value),
		 ColorLerp(c0.blue, c1.blue, value),
		 ColorLerp(c0.alpha, c1.alpha, value)
		 );
	}

	typedef Color4b Color;
}
								  
